#include "RTC.h"

RTC::RTC() {}

void RTC::begin(void) {
  Wire1.begin(21, 22);
  WriteReg(0x00, 0x00);
  WriteReg(0x01, 0x00);
  WriteReg(0x0D, 0x00);
}

void RTC::WriteReg(uint8_t reg, uint8_t data) {
  Wire1.beginTransmission(0x51);
  Wire1.write(reg);
  Wire1.write(data);
  Wire1.endTransmission();
}

uint8_t RTC::ReadReg(uint8_t reg) {
  Wire1.beginTransmission(0x51);
  Wire1.write(reg);
  Wire1.endTransmission(false);
  Wire1.requestFrom(0x51, 1);
  return Wire1.read();
}

void RTC::GetBm8563Time(void) {
  Wire1.beginTransmission(0x51);
  Wire1.write(0x02);
  Wire1.endTransmission(false);
  Wire1.requestFrom(0x51, 7);
  while (Wire1.available()) {
    trdata[0] = Wire1.read();
    trdata[1] = Wire1.read();
    trdata[2] = Wire1.read();
    trdata[3] = Wire1.read();
    trdata[4] = Wire1.read();
    trdata[5] = Wire1.read();
    trdata[6] = Wire1.read();
  }

  DataMask();
  Bcd2asc();
  Str2Time();
}

void RTC::Str2Time(void) {
  Second = (asc[0] - 0x30) * 10 + asc[1] - 0x30;
  Minute = (asc[2] - 0x30) * 10 + asc[3] - 0x30;
  Hour = (asc[4] - 0x30) * 10 + asc[5] - 0x30;
  /*
  uint8_t Hour;
  uint8_t Week;
  uint8_t Day;
  uint8_t Month;
  uint8_t  Year;
  */
}

void RTC::DataMask() {
  trdata[0] = trdata[0] & 0x7f;  //秒
  trdata[1] = trdata[1] & 0x7f;  //分
  trdata[2] = trdata[2] & 0x3f;  //时

  trdata[3] = trdata[3] & 0x3f;  //日
  trdata[4] = trdata[4] & 0x07;  //星期
  trdata[5] = trdata[5] & 0x1f;  //月

  trdata[6] = trdata[6] & 0xff;  //年
}
/********************************************************************
函 数 名： void Bcd2asc(void)
功 能： bcd 码转换成 asc 码，供Lcd显示用
说 明：
调 用：
入口参数：
返 回 值：无
***********************************************************************/
void RTC::Bcd2asc(void) {
  uint8_t i, j;
  for (j = 0, i = 0; i < 7; i++) {
    asc[j++] =
        (trdata[i] & 0xf0) >> 4 | 0x30; /*格式为: 秒 分 时 日 月 星期 年 */
    asc[j++] = (trdata[i] & 0x0f) | 0x30;
  }
}

uint8_t RTC::Bcd2ToByte(uint8_t Value) {
  uint8_t tmp = 0;
  tmp = ((uint8_t)(Value & (uint8_t)0xF0) >> (uint8_t)0x4) * 10;
  return (tmp + (Value & (uint8_t)0x0F));
}

uint8_t RTC::ByteToBcd2(uint8_t Value) {
  uint8_t bcdhigh = 0;

  while (Value >= 10) {
    bcdhigh++;
    Value -= 10;
  }

  return ((uint8_t)(bcdhigh << 4) | Value);
}

void RTC::GetTime(RTC_TimeTypeDef *RTC_TimeStruct) {
  // if()
  uint8_t buf[3] = {0};

  Wire1.beginTransmission(0x51);
  Wire1.write(0x02);
  Wire1.endTransmission(false);
  Wire1.requestFrom(0x51, 3);

  while (Wire1.available()) {
    buf[0] = Wire1.read();
    buf[1] = Wire1.read();
    buf[2] = Wire1.read();
  }

  RTC_TimeStruct->Seconds = Bcd2ToByte(buf[0] & 0x7f);  //秒
  RTC_TimeStruct->Minutes = Bcd2ToByte(buf[1] & 0x7f);  //分
  RTC_TimeStruct->Hours = Bcd2ToByte(buf[2] & 0x3f);    //时
}

int RTC::SetTime(RTC_TimeTypeDef *RTC_TimeStruct) {
  if (RTC_TimeStruct == NULL || RTC_TimeStruct->Hours > 24 ||
      RTC_TimeStruct->Minutes > 60 || RTC_TimeStruct->Seconds > 60)
    return 0;
  Wire1.beginTransmission(0x51);
  Wire1.write(0x02);
  Wire1.write(ByteToBcd2(RTC_TimeStruct->Seconds));
  Wire1.write(ByteToBcd2(RTC_TimeStruct->Minutes));
  Wire1.write(ByteToBcd2(RTC_TimeStruct->Hours));
  Wire1.endTransmission();
  return 1;
}

void RTC::GetDate(RTC_DateTypeDef *RTC_DateStruct) {
  uint8_t buf[4] = {0};

  Wire1.beginTransmission(0x51);
  Wire1.write(0x05);
  Wire1.endTransmission(false);
  Wire1.requestFrom(0x51, 4);

  while (Wire1.available()) {
    buf[0] = Wire1.read();
    buf[1] = Wire1.read();
    buf[2] = Wire1.read();
    buf[3] = Wire1.read();
  }

  RTC_DateStruct->Date = Bcd2ToByte(buf[0] & 0x3f);
  RTC_DateStruct->WeekDay = Bcd2ToByte(buf[1] & 0x07);
  RTC_DateStruct->Month = Bcd2ToByte(buf[2] & 0x1f);

  if (buf[2] & 0x80) {
    RTC_DateStruct->Year = 1900 + Bcd2ToByte(buf[3] & 0xff);
  } else {
    RTC_DateStruct->Year = 2000 + Bcd2ToByte(buf[3] & 0xff);
  }
}

int RTC::SetDate(RTC_DateTypeDef *RTC_DateStruct) {
  if (RTC_DateStruct == NULL || RTC_DateStruct->WeekDay > 7 ||
      RTC_DateStruct->Date > 31 || RTC_DateStruct->Month > 12)
    return 0;
  Wire1.beginTransmission(0x51);
  Wire1.write(0x05);
  Wire1.write(ByteToBcd2(RTC_DateStruct->Date));
  Wire1.write(ByteToBcd2(RTC_DateStruct->WeekDay));

  if (RTC_DateStruct->Year < 2000) {
    Wire1.write(ByteToBcd2(RTC_DateStruct->Month) | 0x80);
    Wire1.write(ByteToBcd2((uint8_t)(RTC_DateStruct->Year % 100)));
  } else {
    /* code */
    Wire1.write(ByteToBcd2(RTC_DateStruct->Month) | 0x00);
    Wire1.write(ByteToBcd2((uint8_t)(RTC_DateStruct->Year % 100)));
  }

  Wire1.endTransmission();
  return 1;
}

int RTC::SetAlarmIRQ(int afterSeconds) {
  uint8_t reg_value = 0;
  reg_value = ReadReg(0x01);

  if (afterSeconds < 0) {
    reg_value &= ~(1 << 0);
    WriteReg(0x01, reg_value);
    reg_value = 0x03;
    WriteReg(0x0E, reg_value);
    return -1;
  }

  uint8_t type_value = 2;
  uint8_t div = 1;
  if (afterSeconds > 255) {
    div = 60;
    type_value = 0x83;
  } else {
    type_value = 0x82;
  }

  afterSeconds = (afterSeconds / div) & 0xFF;
  WriteReg(0x0F, afterSeconds);
  WriteReg(0x0E, type_value);

  reg_value |= (1 << 0);
  reg_value &= ~(1 << 7);
  WriteReg(0x01, reg_value);
  return afterSeconds * div;
}

int RTC::SetAlarmIRQ(const RTC_TimeTypeDef &RTC_TimeStruct) {
  uint8_t irq_enable = false;
  uint8_t out_buf[4] = {0x80, 0x80, 0x80, 0x80};

  if (RTC_TimeStruct.Minutes >= 0) {
    irq_enable = true;
    out_buf[0] = ByteToBcd2(RTC_TimeStruct.Minutes) & 0x7f;
  }

  if (RTC_TimeStruct.Hours >= 0) {
    irq_enable = true;
    out_buf[1] = ByteToBcd2(RTC_TimeStruct.Hours) & 0x3f;
  }

  out_buf[2] = 0x00;
  out_buf[3] = 0x00;

  uint8_t reg_value = ReadReg(0x01);

  if (irq_enable) {
    reg_value |= (1 << 1);
  } else {
    reg_value &= ~(1 << 1);
  }

  for (int i = 0; i < 4; i++) {
    WriteReg(0x09 + i, out_buf[i]);
  }
  WriteReg(0x01, reg_value);

  return irq_enable ? 1 : 0;
}

int RTC::SetAlarmIRQ(const RTC_DateTypeDef &RTC_DateStruct,
                     const RTC_TimeTypeDef &RTC_TimeStruct) {
  uint8_t irq_enable = false;
  uint8_t out_buf[4] = {0x80, 0x80, 0x80, 0x80};

  if (RTC_TimeStruct.Minutes >= 0) {
    irq_enable = true;
    out_buf[0] = ByteToBcd2(RTC_TimeStruct.Minutes) & 0x7f;
  }

  if (RTC_TimeStruct.Hours >= 0) {
    irq_enable = true;
    out_buf[1] = ByteToBcd2(RTC_TimeStruct.Hours) & 0x3f;
  }

  if (RTC_DateStruct.Date >= 0) {
    irq_enable = true;
    out_buf[2] = ByteToBcd2(RTC_DateStruct.Date) & 0x3f;
  }

  if (RTC_DateStruct.WeekDay >= 0) {
    irq_enable = true;
    out_buf[3] = ByteToBcd2(RTC_DateStruct.WeekDay) & 0x07;
  }

  uint8_t reg_value = ReadReg(0x01);

  if (irq_enable) {
    reg_value |= (1 << 1);
  } else {
    reg_value &= ~(1 << 1);
  }

  for (int i = 0; i < 4; i++) {
    WriteReg(0x09 + i, out_buf[i]);
  }
  WriteReg(0x01, reg_value);

  return irq_enable ? 1 : 0;
}

void RTC::clearIRQ() {
  uint8_t data = ReadReg(0x01);
  WriteReg(0x01, data & 0xf3);
}
void RTC::disableIRQ() {
  clearIRQ();
  uint8_t data = ReadReg(0x01);
  WriteReg(0x01, data & 0xfC);
}
